import 'dart:io';

import 'package:dio/adapter.dart';
import 'package:flutter_baselib/flutter_baselib.dart';

///@date:  2021/2/25 14:25
///@author:  lixu
///@description: 网络请求管理单例
class XApi {
  static const _tag = 'XApi';
  static const contentTypeJson = Headers.jsonContentType;
  static const contentTypeForm = Headers.formUrlEncodedContentType;
  static const methodPost = "POST";
  static const methodGet = "GET";
  static const downLoadReceiveTimeout = 60 * 1000;

  static XApi _instance = XApi._();
  static late IHttpConfig _httpConfig;
  static late Dio _dio;
  List<CancelToken> _cancelTokenList = [];

  ///初始化网络相关参数
  static void init(IHttpConfig httpConfig) {
    HttpCode.successCodeList.addAll(httpConfig.configHttpResultSuccessCodeList());

    _httpConfig = httpConfig;
    _dio = Dio(_httpConfig.configBaseOptions());

    ///拦截器队列的执行顺序是FIFO
    ///添加自定义拦截器
    if (_httpConfig.configInterceptors() != null) {
      _dio.interceptors.addAll(_httpConfig.configInterceptors()!);
    }

    ///添加默认的日志拦截器
    if (_httpConfig.configLogEnable()) {
      _dio.interceptors.add(LogsInterceptors());
    }

    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        ///true：忽略证书校验
        return _httpConfig.configHttps(cert, host, port);
      };
    };
  }

  factory XApi() {
    return _instance;
  }

  XApi._();

  ///下载文件
  /// [url] 下载地址
  /// [savePath] 本地保存的地址
  /// [receiveTimeout] 下载文件超时时间ms
  /// [options] 针对当前请求的配置选项
  /// [onReceiveProgress] 下载进度
  /// return 是否下载成功
  Future<bool> downloadFile(
    String url,
    String savePath, {
    int? receiveTimeout = downLoadReceiveTimeout,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
  }) async {
    LogUtils.i(_tag, 'downloadFile() url:$url  savePath:$savePath');
    bool isSuccess;

    ///添加CancelToken,用于取消请求
    cancelToken ??= CancelToken();
    _cancelTokenList.add(cancelToken);

    ///设置超时时间
    if (receiveTimeout != null) {
      options ??= Options();
      options.receiveTimeout = receiveTimeout;
    }

    try {
      await _dio.download(url, savePath, cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, options: options);
      isSuccess = true;
      LogUtils.i(_tag, 'downloadFile() success, url:$url  savePath:$savePath');
    } catch (e) {
      LogUtils.e(_tag, 'downloadFile fail:$e');
      isSuccess = false;
    }

    ///请求完成移除cancelToken
    _cancelTokenList.remove(cancelToken);
    return isSuccess;
  }

  ///发起请求,响应data为单个对象
  ///[url] 请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗，默认true
  ///[loadingText] 加载提示，默认“loading”
  ///[isCancelableDialog] 加载中能否关闭加载弹窗，默认false
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误，默认true
  ///[isCheckNetwork] 请求前是否校验网络连接，默认true
  ///[onSuccess] 请求成功回调
  ///[onError] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future request<T>(String url,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      String? method,
      Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      bool? isCheckNetwork,
      CancelToken? cancelToken,
      Function(T?)? onSuccess,
      Function(HttpErrorBean)? onError,
      Function()? onComplete}) async {
    return _commonRequest<T>(url, false,
        params: params,
        header: header,
        method: method,
        option: option,
        isShowLoading: isShowLoading,
        loadingText: loadingText,
        isCancelableDialog: isCancelableDialog,
        isShowFailToast: isShowFailToast,
        isCheckNetwork: isCheckNetwork,
        cancelToken: cancelToken,
        onSuccessObjCallback: onSuccess,
        onErrorCallback: onError,
        onCompleteCallback: onComplete);
  }

  ///发起请求,响应data为List
  ///[url]请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗，默认true
  ///[loadingText] 加载提示，默认“loading”
  ///[isCancelableDialog] 加载中能否关闭加载弹窗，默认false,关闭弹窗同时取消请求
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误，默认true
  ///[isCheckNetwork] 请求前是否校验网络连接，默认true
  ///[onSuccess] 请求成功回调
  ///[onError] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future requestList<T>(String url,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      String? method,
      Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      bool? isCheckNetwork,
      CancelToken? cancelToken,
      Function(List<T>?)? onSuccess,
      Function(HttpErrorBean)? onError,
      Function()? onComplete}) async {
    return _commonRequest<T>(url, true,
        params: params,
        header: header,
        method: method,
        option: option,
        isShowLoading: isShowLoading,
        loadingText: loadingText,
        isCancelableDialog: isCancelableDialog,
        isShowFailToast: isShowFailToast,
        isCheckNetwork: isCheckNetwork,
        cancelToken: cancelToken,
        onSuccessListCallback: onSuccess,
        onErrorCallback: onError,
        onCompleteCallback: onComplete);
  }

  ///通用http请求
  ///[url] 请求url，如果 `url` 以 "http(s)"开始, 则 `baseURL` 会被忽略； 否则,将会和baseUrl拼接出完整的的url.
  ///[params] 请求参数,可为空
  ///[isResultList] 返回的data是否是List类型
  ///[header] 请求头
  ///[method] 请求方法，优先级最高
  ///[option] 针对当前请求的配置选项，优先级次高
  ///[isShowLoading] 是否显示加载弹窗
  ///[loadingText] 加载提示
  ///[isCancelableDialog] 加载中能否关闭加载弹窗
  ///[isShowFailToast] 请求失败时是否自动显示toast提示错误
  ///[isCheckNetwork] 请求前是否校验网络连接
  ///[onSuccessListCallback] 请求List成功回调
  ///[onSuccessObjCallback] 请求单个对象成功回调
  ///[onErrorCallback] 请求失败回调
  ///[onComplete] 请求完成回调，在onSuccess或onError方法后面调用
  Future _commonRequest<T>(String url, bool isResultList,
      {Map<String, dynamic>? params,
      Map<String, dynamic>? header,
      String? method,
      Options? option,
      bool? isShowLoading,
      String? loadingText,
      bool? isCancelableDialog,
      bool? isShowFailToast,
      bool? isCheckNetwork,
      CancelToken? cancelToken,
      Function(List<T>?)? onSuccessListCallback,
      Function(T?)? onSuccessObjCallback,
      Function(HttpErrorBean)? onErrorCallback,
      Function()? onCompleteCallback}) async {
    ///设置默认值
    isShowLoading ??= _httpConfig.isShowLoading();
    loadingText ??= _httpConfig.configLoadingText();
    isCancelableDialog ??= _httpConfig.isCancelableDialog();
    isShowFailToast ??= _httpConfig.isShowFailToast();
    isCheckNetwork ??= _httpConfig.isCheckNetwork();

    if (isCheckNetwork) {
      ///判断网络连接
      ConnectivityResult connResult = await Connectivity().checkConnectivity();
      if (connResult == ConnectivityResult.none) {
        _onRespErrorCallback(
          isShowFailToast,
          onErrorCallback,
          HttpErrorBean(code: HttpCode.networkError, message: '无网络连接，请检查网络设置'),
        );

        ///请求完成回调
        onCompleteCallback?.call();
        return;
      }
    }

    option ??= Options();

    ///添加baseUrl
    ///baseUrl优先级：形参url>_httpConfig.getBaseUrl>_httpConfig.configBaseOptions
    if (!url.startsWith(Constants.httpStartWith)) {
      String? baseUrl = await _httpConfig.getBaseUrl(url);
      if (baseUrl != null && baseUrl.isNotEmpty) {
        url = baseUrl + url;
      }
    }

    params ??= {};

    ///添加CancelToken,用于取消请求
    cancelToken ??= CancelToken();
    _cancelTokenList.add(cancelToken);

    ///处理请求头
    if (header != null) {
      option.headers ??= {};
      option.headers!.addAll(header);
    }

    ///设置请求方法
    if (method != null) {
      option.method = method;
    } else {
      option.method ??= _httpConfig.configBaseOptions().method;
      option.method ??= methodPost;
    }

    ///显示加载框
    if (isShowLoading) {
      _httpConfig.showLoading(url, cancelToken.hashCode, cancelToken, loadingText, isCancelableDialog);
    }

    try {
      Response response;
      if (methodGet == option.method) {
        ///get请求
        response = await _dio.get(url, queryParameters: params, options: option, cancelToken: cancelToken);
      } else if (methodPost == option.method) {
        ///默认post请求
        response = await _dio.post(url, data: params, options: option, cancelToken: cancelToken);
      } else {
        ///其他请求方式
        response = await _dio.request(url, data: params, options: option, cancelToken: cancelToken);
      }

      HttpResultBean<T> resultBean = await _parseJsonToObject<T>(url, response, isResultList);
      if (resultBean.isSuccess()) {
        _onRespSuccessCallback<T>(resultBean, onSuccessObjCallback, onSuccessListCallback);
      } else {
        _onRespErrorCallback(isShowFailToast, onErrorCallback, resultBean.obtainErrorBean());
      }
    } on DioError catch (e) {
      _onRespErrorCallback(isShowFailToast, onErrorCallback, _createErrorEntity(e));
    } catch (exception) {
      LogUtils.e(_tag, ' 异常:${exception.toString()}');
      _onRespErrorCallback(
        isShowFailToast,
        onErrorCallback,
        HttpErrorBean(code: HttpCode.fail, message: exception.toString()),
      );
    } finally {
      ///请求完成，隐藏加载框
      if (isShowLoading) {
        _httpConfig.hideLoading(url, cancelToken.hashCode, cancelToken.isCancelled);
      }

      ///请求完成移除cancelToken
      if (_cancelTokenList.contains(cancelToken)) {
        _cancelTokenList.remove(cancelToken);
      }

      ///请求完成回调
      onCompleteCallback?.call();
    }
  }

  ///http响应json解析为对象
  ///[response] http 响应的对象
  ///[isRespListData] http响应的数据是否是List数据结构
  Future<HttpResultBean<T>> _parseJsonToObject<T>(String url, Response response, bool isRespListData) async {
    if (response.data == null) {
      HttpResultBean<T> resultBean = HttpResultBean();
      resultBean.isRespListData = isRespListData;
      resultBean.code = HttpCode.unKnowError;
      resultBean.message = 'response is null';
      return resultBean;
    }

    HttpResultBean<T> resultBean = await _httpConfig.parseJsonToObject<T>(url, response.data, isRespListData);
    resultBean.json = response.data;
    return resultBean;
  }

  ///http响应成功回调
  void _onRespSuccessCallback<T>(HttpResultBean<T> resultBean, Function(T?)? onSuccessObjCallback, Function(List<T>?)? onSuccessListCallback) {
    try {
      if (resultBean.isRespListData) {
        onSuccessListCallback?.call(resultBean.dataList);
      } else {
        onSuccessObjCallback?.call(resultBean.data);
      }
    } catch (e) {
      LogUtils.e(_tag, '_onRespSuccessCallback() 方法回调异常:$e}');
    }
  }

  ///http响应错误回调
  Future<void> _onRespErrorCallback(bool isShowFailToast, Function(HttpErrorBean)? onErrorCallback, HttpErrorBean errorBean) async {
    if (isShowFailToast && HttpCode.cancel != errorBean.code) {
      ///主动取消请求时不显示错误信息
      _httpConfig.showFailToast(errorBean);
    }

    try {
      onErrorCallback?.call(errorBean);
    } catch (e) {
      LogUtils.e(_tag, '_onRespErrorCallback() 方法回调异常:$e}');
    }

    ///判断token是否失效
    if (await _httpConfig.isHttpRespTokenError(errorBean)) {
      _httpConfig.onTokenErrorCallback(errorBean);
    }
  }

  ///封装错误bean
  HttpErrorBean _createErrorEntity(DioError error) {
    switch (error.type) {
      case DioErrorType.cancel:
        return HttpErrorBean(code: HttpCode.cancel, message: "请求取消");

      case DioErrorType.connectTimeout:
        return HttpErrorBean(code: HttpCode.connectTimeout, message: "连接超时");

      case DioErrorType.sendTimeout:
        return HttpErrorBean(code: HttpCode.sendTimeout, message: "请求超时");

      case DioErrorType.receiveTimeout:
        return HttpErrorBean(code: HttpCode.receiveTimeout, message: "响应超时");

      case DioErrorType.response:
        String errCode = error.response?.statusCode?.toString() ?? HttpCode.unKnowError;
        String errMsg = error.response?.statusMessage ?? '未知错误';
        return HttpErrorBean(code: errCode, message: errMsg);

      default:
        return HttpErrorBean(code: HttpCode.unKnowError, message: error.message);
    }
  }

  ///取消所有请求
  void cancelAll() {
    LogUtils.i(_tag, 'cancelAll:${_cancelTokenList.length}');
    _cancelTokenList.forEach((cancelToken) {
      cancel(cancelToken);
    });
    _cancelTokenList.clear();
  }

  ///取消指定的请求
  void cancel(CancelToken? cancelToken) {
    if (cancelToken != null && !cancelToken.isCancelled) {
      cancelToken.cancel();
    }
  }

  ///取消指定的请求
  void cancelList(List<CancelToken>? cancelTokenList) {
    LogUtils.i(_tag, 'cancelList:${cancelTokenList?.length}');
    cancelTokenList?.forEach((cancelToken) {
      cancel(cancelToken);
    });
  }
}
